package util

// 可以把sku生成图，然后判断哪个sku下可以选择其他的sku, 具体可以看测试文件
import (
	"errors"
	"fmt"
	"github.com/vmihailenco/msgpack/v5"
	"strings"
)

type Graph struct {
	size         int                  // 顶点数据量
	vertex       []*Node              // 存储顶点数据
	vertexOffset int                  // 已添加的顶点的位置
	vertexStr    map[int]string       // map[0:4G 1:8G 2:16G 3:灰色 4:白色 5:i5 6:i7]  所有属性对应偏移量的值
	vertexPos    map[string]int       // 记录顶点数据中元素的位置
	attrs        map[int64]*GraphAttr // 存储顶点数据的同级 map[属性id] = 属性
	//types        map[int][]string // 存储顶点数据的同级
	//typesOffset  int              // 同级数据的位置
	matrix    []byte              // 存储邻接矩阵的数据
	multiAtts map[string][]string // 根据不同的选择获取选择可选元素
}

type Node struct {
	Super     int64
	Data      string
	isTypes   bool
	isVisited bool
}

type GraphAttr struct {
	Title string                   // 标签名称
	Attrs map[string]*GraphAttrVal // 存储详细的属性值信息，map[ids]=*GraphAttrVal
}

type GraphAttrVal struct {
	Super  int64  // 上级id
	Title  string // 标签，属性值的名称
	Ids    string // 组合的ids=》属性:属性值的id
	Status byte   // 这是用于判断，选中的状态
}

func NewGraph(size int) (g *Graph) {
	g = &Graph{
		size:         size,
		vertexOffset: 0,
		//typesOffset:  0,
	}
	g.vertex = make([]*Node, size)
	g.vertexPos = make(map[string]int, size)
	g.vertexStr = make(map[int]string, size)
	g.attrs = make(map[int64]*GraphAttr)
	g.multiAtts = make(map[string][]string)
	g.matrix = make([]byte, size*size)

	return g
}

func (g *Graph) AddAttrs(attr string, attrVal *GraphAttrVal) {
	attrVal.Status = 0

	if g.attrs[attrVal.Super] == nil { // 判断上级是否存在
		g.attrs[attrVal.Super] = &GraphAttr{
			Title: attr,
		}
		g.attrs[attrVal.Super].Attrs = make(map[string]*GraphAttrVal)
	}
	g.attrs[attrVal.Super].Attrs[attrVal.Ids] = attrVal
	//g.types[g.typesOffset] = list
	//
	//for _, data := range list {
	//	g.AddNode(data)
	//}
	//g.typesOffset++

}

// AddNode 记录顶点节点
func (g *Graph) AddNode(super int64, ids string, data string) {
	g.vertexPos[ids] = g.vertexOffset // 属性对应位置
	g.vertexStr[g.vertexOffset] = ids // 位置对应属性
	g.vertex[g.vertexOffset] = &Node{ // 位置对应属性节点
		Data:      data,
		isTypes:   false,
		isVisited: false,
		Super:     super,
	}
	g.vertexOffset++ // 位置++
}

// AddVertex 添加属性
// {"4G", "灰色", "i5"},
// {"4G", "白色", "i5"},
func (g *Graph) AddVertex(val []string) error {
	if len(val) < 1 {
		return errors.New("数据不足无法定位")
	}

	for i := 0; i < len(val); i++ { // row
		row := g.vertexPos[val[i]] // 取属性横位置
		// 同级的添加
		g.sameLevel(val[i], row)
		for j := i + 1; j < len(val); j++ { // col、
			if err := g.matrixs(row, g.vertexPos[val[j]]); err != nil { // 标记可选属性
				return err
			}
		}
	}

	// 判断是否考虑多级
	if len(val) > 2 {
		g.addMultiAttr(val)
	}
	return nil
}

// 同级的添加标记
func (g *Graph) sameLevel(item string, row int) error {
	//row := g.vertexPos[item]
	if g.vertex[row].isTypes {
		return nil
	}
	// 标记同级
	for _, v := range g.attrs[g.vertex[row].Super].Attrs {
		if v.Ids == item {
			continue
		}
		if err := g.matrixs(row, g.vertexPos[v.Ids]); err != nil {
			return err
		}
	}
	g.vertex[row].isTypes = true
	return nil
}

// 设置多级选择
func (g *Graph) addMultiAttr(val []string) {
	var key strings.Builder
	key.Grow(len(val))
	for i := 2; i < len(val); i++ { // 选中的元素个数
		for j := 0; j < len(val)-1; j++ { // 计算选中的可能性
			// 防止出现超标的异常
			if j+i > len(val) {
				return
			}
			// 1,2,3,4
			for _, v := range val[j : j+i] {
				key.WriteString(v)
			}

			if tmp := val[j+i:]; len(tmp) > 0 {
				g.multiAtts[key.String()] = append(g.multiAtts[key.String()], tmp...)
			}
			if tmp := val[j+i:]; len(tmp) > 0 {
				g.multiAtts[key.String()] = append(g.multiAtts[key.String()], tmp...)
			}

			key.Reset()
		}
	}
}

func (g *Graph) MatrixValue(row, col int) byte {
	// row * g.size + col ：可以确定属性在切片里面的具体位置
	// 如下图  row 相当于纵坐标  col相当于横坐标  g.size是每行的个数
	// 比如 row = 5 col = 6（从0开始） ，g.size = 7，则在下图的位置就是 i5=0的值
	//         4G      8G      16G     灰色    白色    i5      i7
	//4G      0       1       1       1       1       1       0
	//8G      1       0       1       1       0       0       1
	//16G     1       1       0       0       1       1       0
	//灰色    1       1       0       0       1       1       1
	//白色    1       0       1       1       0       1       0
	//i5      1       0       1       1       1       0       1
	//i7      0       1       0       1       0       1       0
	return g.matrix[row*g.size+col]
}

func (g *Graph) attrStatus(types map[int64]*GraphAttr, col int, ids string) {
	types[g.vertex[col].Super].Attrs[ids].Status = 1
}

// 对称标记
func (g *Graph) matrixs(row, col int) error {
	if row < 0 || row >= g.size || col < 0 || col > g.size {
		return errors.New("数据异常，无法定位")
	}
	// 横竖都要标记
	g.matrix[row*g.size+col] = 1
	g.matrix[col*g.size+row] = 1

	return nil
}

// Collection 属性的筛选：多属性
func (g *Graph) Collection11(val ...string) (types map[int64]*GraphAttr) {
	vertexs := g.vertexStr // 位置对应的属性
	types = g.attrs
	//fmt.Println(vertexs)
	for _, v := range val {
		col := g.vertexPos[v] // 取出属性所在位置
		//fmt.Println(col)
		//types[g.vertex[col].Super].Attrs[vertexs[col]].Status = 1
		g.vertex[col].isVisited = true
		for i, _ := range vertexs { // 取出每个属性对应的位置
			if g.vertex[i].isVisited {
				g.attrStatus(types, col, vertexs[col])
				continue
			}
			if _, ok := vertexs[i]; ok && g.MatrixValue(i, col) == 0 { // 判断属性对应的值是否可选
				delete(vertexs, i)
			} else {
				g.attrStatus(types, i, vertexs[i])
			}
		}
		g.vertex[col].isVisited = false
	}
	return
}

func (g *Graph) Collection(val ...string) (types map[int64]*GraphAttr) {
	//fmt.Println("collection val => ", val)
	types = g.attrs
	// 当我们选中的元素为空则，所有都可以选
	if len(val) == 1 && val[0] == "" {
		for i, attr := range types {
			for k, _ := range attr.Attrs {
				types[i].Attrs[k].Status = 1
			}
		}
		return
	}
	// 适合于两个属性，或者查询的val是少3个
	if len(val) < 2 || len(g.attrs) < 3 {

		// 可选和不可选
		vertexs := g.vertexStr

		for _, v := range val { // 当前查询元素

			col := g.vertexPos[v]
			// 标记当前自己的元素

			g.vertex[col].isVisited = true

			for i, _ := range vertexs { // 匹配可选元素
				if g.vertex[i].isVisited {
					g.attrStatus(types, col, vertexs[col])
					continue
				}
				if _, ok := vertexs[i]; ok && g.MatrixValue(i, col) == 0 {
					delete(vertexs, i)
				} else {
					g.attrStatus(types, i, vertexs[i])
				}
			}

			g.vertex[col].isVisited = false
		}

		return
	}
	// 是多属性的查询
	var key strings.Builder

	for _, v := range val {
		g.attrStatus(types, g.vertexPos[v], v)
		key.WriteString(v)
	}

	for _, v := range g.multiAtts[key.String()] {
		g.attrStatus(types, g.vertexPos[v], v)
	}

	return
}

// Print 打印
func (g *Graph) Print() {
	fmt.Print("\t")
	for i := 0; i < g.size; i++ {
		fmt.Print(g.vertex[i].Data, "\t")
	}

	fmt.Println()
	for i := 0; i < g.size; i++ {
		fmt.Print(g.vertex[i].Data, "\t")
		for j := 0; j < g.size; j++ {
			fmt.Print(g.matrix[i*g.size+j], "\t")
		}
		fmt.Println()
	}
}

type CopyGraph struct {
	Size int // 顶点数据量

	Vertex    []*Node // 存储顶点数据
	VertexStr map[int]string
	VertexPos map[string]int // 记录无序的顶点数据中元素的位置

	Attrs     map[int64]*GraphAttr // 根据属性id存储
	MultiAtts map[string][]string  // 根据不同的选择获取选择可选元素
	Matrix    []byte               // 存储邻接矩阵的数据
}

func newCopyGraph(g *Graph) *CopyGraph {
	return &CopyGraph{
		Size:      g.size,
		Vertex:    g.vertex,
		VertexStr: g.vertexStr,
		VertexPos: g.vertexPos,
		Attrs:     g.attrs,
		MultiAtts: g.multiAtts,
		Matrix:    g.matrix,
	}
}

func (g *CopyGraph) newGraph() *Graph {
	return &Graph{
		size:         g.Size,
		vertex:       g.Vertex,
		vertexOffset: 0,
		vertexStr:    g.VertexStr,
		vertexPos:    g.VertexPos,
		attrs:        g.Attrs,
		multiAtts:    g.MultiAtts,
		matrix:       g.Matrix,
	}
}

func (g *Graph) ExportMsgPack() []byte {
	b, _ := msgpack.Marshal(newCopyGraph(g))
	return b
}

func LoadGraphMsgPack(d []byte) (*Graph, error) {
	tmp := new(CopyGraph)
	if err := msgpack.Unmarshal(d, tmp); err != nil {
		return nil, err
	}
	return tmp.newGraph(), nil
}
